1
2
3
4
5
6
7
8
9
10
11
12
13 package org.apache.tapestry5.internal.pageload;
14
15 import org.apache.tapestry5.internal.TapestryInternalUtils;
16 import org.apache.tapestry5.internal.services.ComponentInstantiatorSource;
17 import org.apache.tapestry5.internal.services.Instantiator;
18 import org.apache.tapestry5.internal.structure.ComponentPageElement;
19 import org.apache.tapestry5.ioc.Location;
20 import org.apache.tapestry5.ioc.Orderable;
21 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
22 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
23 import org.apache.tapestry5.ioc.internal.util.TapestryException;
24 import org.apache.tapestry5.model.ComponentModel;
25 import org.apache.tapestry5.model.EmbeddedComponentModel;
26 import org.apache.tapestry5.services.ComponentClassResolver;
27 import org.apache.tapestry5.services.pageload.ComponentResourceSelector;
28
29 import java.util.HashSet;
30 import java.util.Map;
31 import java.util.Set;
32
33 public class EmbeddedComponentAssemblerImpl implements EmbeddedComponentAssembler
34 {
35 private final ComponentInstantiatorSource instantiatorSource;
36
37 private final ComponentAssemblerSource assemblerSource;
38
39 private final ComponentResourceSelector selector;
40
41 private final ComponentModel componentModel;
42
43 private final Location location;
44
45 private final Map<String, Instantiator> mixinIdToInstantiator = CollectionFactory.newCaseInsensitiveMap();
46
47 private final Map<String, String[]> mixinsIdToOrderConstraints = CollectionFactory.newCaseInsensitiveMap();
48
49
50
51
52 private final Map<String, ParameterBinder> parameterNameToBinder = CollectionFactory.newCaseInsensitiveMap();
53
54
55
56
57 private final String informalParametersMixinId;
58
59 private final String componentPsuedoMixinId;
60
61 private Map<String, Boolean> bound;
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public EmbeddedComponentAssemblerImpl(ComponentAssemblerSource assemblerSource,
83 ComponentInstantiatorSource instantiatorSource, ComponentClassResolver componentClassResolver,
84 String componentClassName, ComponentResourceSelector selector, EmbeddedComponentModel embeddedModel,
85 String templateMixins, Location location, boolean strictMixinParameters)
86 {
87 this.assemblerSource = assemblerSource;
88 this.instantiatorSource = instantiatorSource;
89 this.selector = selector;
90 this.location = location;
91
92 componentModel = getModel(componentClassName);
93
94
95
96 for (String className : componentModel.getMixinClassNames())
97 {
98 addMixin(className, componentModel.getOrderForMixin(className));
99 }
100
101
102
103
104 if (embeddedModel != null)
105 {
106 for (String className : embeddedModel.getMixinClassNames())
107 {
108 addMixin(className, embeddedModel.getConstraintsForMixin(className));
109 }
110 }
111
112
113
114 for (String mixinDef : TapestryInternalUtils.splitAtCommas(templateMixins))
115 {
116 Orderable<String> order = TapestryInternalUtils.mixinTypeAndOrder(mixinDef);
117 String className = componentClassResolver.resolveMixinTypeToClassName(order.getId());
118
119 addMixin(className, order.getConstraints());
120 }
121
122
123
124
125
126 componentPsuedoMixinId = InternalUtils.lastTerm(componentClassName);
127
128
129
130 informalParametersMixinId = prescanMixins(strictMixinParameters);
131 }
132
133 private String prescanMixins(boolean strictMixinParameters)
134 {
135
136
137 String supportsInformals = null;
138
139 for (Map.Entry<String, Instantiator> entry : mixinIdToInstantiator.entrySet())
140 {
141 String mixinId = entry.getKey();
142 ComponentModel mixinModel = entry.getValue().getModel();
143
144 updateParameterNameToQualified(mixinId, mixinModel, strictMixinParameters);
145
146 if (supportsInformals == null && mixinModel.getSupportsInformalParameters())
147 supportsInformals = mixinId;
148 }
149
150
151
152 updateParameterNameToQualified(null, componentModel, false);
153
154 return supportsInformals;
155 }
156
157 private void updateParameterNameToQualified(String mixinId, ComponentModel model, boolean strictMixinParameters)
158 {
159 for (String parameterName : model.getParameterNames())
160 {
161 String defaultBindingPrefix = model.getParameterModel(parameterName).getDefaultBindingPrefix();
162
163 ParameterBinderImpl binder = new ParameterBinderImpl(mixinId, parameterName, defaultBindingPrefix);
164
165 if (mixinId == null)
166 {
167
168 parameterNameToBinder.put(parameterName, binder);
169 } else
170 {
171
172 parameterNameToBinder.put(mixinId + "." + parameterName, binder);
173
174
175 if (!strictMixinParameters)
176 {
177 parameterNameToBinder.put(parameterName, binder);
178 }
179 }
180 }
181 }
182
183 private void addMixin(String className, String... order)
184 {
185 Instantiator mixinInstantiator = instantiatorSource.getInstantiator(className);
186
187 String mixinId = InternalUtils.lastTerm(className);
188
189 if (mixinIdToInstantiator.containsKey(mixinId))
190 throw new TapestryException(String.format("Mixins applied to a component must be unique. Mixin '%s' has already been applied.", mixinId), location, null);
191
192 mixinIdToInstantiator.put(mixinId, mixinInstantiator);
193 mixinsIdToOrderConstraints.put(mixinId, order);
194 }
195
196 private ComponentModel getModel(String className)
197 {
198 return instantiatorSource.getInstantiator(className).getModel();
199 }
200
201 public ComponentAssembler getComponentAssembler()
202 {
203 return assemblerSource.getAssembler(componentModel.getComponentClassName(), selector);
204 }
205
206
207 public ParameterBinder createParameterBinder(String qualifiedParameterName)
208 {
209 int dotx = qualifiedParameterName.indexOf('.');
210
211 if (dotx < 0)
212 {
213 return createParameterBinderFromSimpleParameterName(qualifiedParameterName);
214 }
215
216 return createParameterBinderFromQualifiedParameterName(qualifiedParameterName, qualifiedParameterName.substring(0, dotx),
217 qualifiedParameterName.substring(dotx + 1));
218 }
219
220 private ParameterBinder createParameterBinderFromSimpleParameterName(String parameterName)
221 {
222
223
224
225 ParameterBinder binder = getComponentAssembler().getBinder(parameterName);
226
227 if (binder != null)
228 {
229 return binder;
230 }
231
232
233
234 binder = parameterNameToBinder.get(parameterName);
235
236 if (binder != null)
237 {
238 return binder;
239 }
240
241
242
243
244 if (informalParametersMixinId != null)
245 {
246 return new ParameterBinderImpl(informalParametersMixinId, parameterName, null);
247 }
248
249
250 if (componentModel.getSupportsInformalParameters())
251 return new ParameterBinderImpl(null, parameterName, null);
252
253
254
255 return null;
256 }
257
258 private ParameterBinder createParameterBinderFromQualifiedParameterName(String qualifiedParameterName, String mixinId, String parameterName)
259 {
260
261 if (mixinId.equalsIgnoreCase(componentPsuedoMixinId))
262 {
263 return createParameterBinderForComponent(qualifiedParameterName, parameterName);
264 }
265
266 if (!mixinIdToInstantiator.containsKey(mixinId))
267 {
268 throw new TapestryException(
269 String.format("Mixin id for parameter '%s' not found. Attached mixins: %s.", qualifiedParameterName,
270 InternalUtils.joinSorted(mixinIdToInstantiator.keySet())), location,
271 null);
272 }
273
274 ParameterBinder binder = parameterNameToBinder.get(qualifiedParameterName);
275
276 if (binder != null)
277 {
278 return binder;
279 }
280
281
282
283 Instantiator instantiator = mixinIdToInstantiator.get(mixinId);
284
285 assert instantiator != null;
286
287 return bindInformalParameter(qualifiedParameterName, mixinId, parameterName, instantiator.getModel());
288 }
289
290 private ParameterBinder bindInformalParameter(String qualifiedParameterName, String mixinId, String parameterName, ComponentModel model)
291 {
292 if (model.getSupportsInformalParameters())
293 {
294 return new ParameterBinderImpl(mixinId, parameterName, null);
295 }
296
297
298
299 throw new TapestryException(String.format("Binding parameter %s as an informal parameter does not make sense, as %s does not support informal parameters.",
300 qualifiedParameterName, model.getComponentClassName()), location, null);
301 }
302
303 private ParameterBinder createParameterBinderForComponent(String qualifiedParameterName, String parameterName)
304 {
305 ParameterBinder binder = getComponentAssembler().getBinder(parameterName);
306
307 if (binder != null)
308 {
309 return binder;
310 }
311
312 return bindInformalParameter(qualifiedParameterName, null, parameterName, componentModel);
313 }
314
315
316 public boolean isBound(String parameterName)
317 {
318 return InternalUtils.get(bound, parameterName) != null;
319 }
320
321 public void setBound(String parameterName)
322 {
323 if (bound == null)
324 bound = CollectionFactory.newCaseInsensitiveMap();
325
326 bound.put(parameterName, true);
327 }
328
329 public int addMixinsToElement(ComponentPageElement newElement)
330 {
331 for (Map.Entry<String, Instantiator> entry : mixinIdToInstantiator.entrySet())
332 {
333 String mixinId = entry.getKey();
334 Instantiator instantiator = entry.getValue();
335
336 newElement.addMixin(mixinId, instantiator, mixinsIdToOrderConstraints.get(mixinId));
337 }
338
339 return mixinIdToInstantiator.size();
340 }
341
342 public Location getLocation()
343 {
344 return location;
345 }
346
347 public Set<String> getFormalParameterNames()
348 {
349 return new HashSet<String>(componentModel.getParameterNames());
350 }
351 }